package wci.frontend.c.tokens;

import wci.frontend.*;
import wci.frontend.c.*;

import static wci.frontend.Source.EOL;
import static wci.frontend.Source.EOF;
import static wci.frontend.c.CTokenType.*;
import static wci.frontend.c.CErrorCode.*;

/**
 * <h1>CStringToken</h1>
 *
 * <p> C string tokens.</p>
 *
 *	-- originally PascalStringToken -- 
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 * 
 * @author Greg Drysdale
 */
public class CStringToken extends CToken
{
    /**
     * Constructor.
     * @param source the source from where to fetch the token's characters.
     * @throws Exception if an error occurred.
     */
    public CStringToken(Source source)
        throws Exception
    {
        super(source);
    }

    /**
     * Extract a C string token from the source.
     * @throws Exception if an error occurred.
     */
    protected void extract()
        throws Exception
    {
        StringBuilder textBuffer = new StringBuilder();
        StringBuilder valueBuffer = new StringBuilder();
        boolean invalidEscapeSequence = false;

        char currentChar = nextChar();  // consume initial quote
        textBuffer.append('\"');
        
        while ((currentChar != '\"') && (currentChar != EOF) && (currentChar != EOL))
        {
        	// Replace any whitespace character with a blank.
            if (Character.isWhitespace(currentChar)) {
            	textBuffer.append(" ");
            	valueBuffer.append(" ");
            }
            // Escaped character
            else if (currentChar == '\\')
            {
            	switch(peekChar())
            	{
            	case 'b': // backspace
            		textBuffer.append("\\b");
                	valueBuffer.append("\b");
                	break;
            	case 'n': // newline
            		textBuffer.append("\\n");
                	valueBuffer.append("\n");
                	break;
            	case 'r': // carriage return
            		textBuffer.append("\\r");
                	valueBuffer.append("\r");
                	break;
            	case '\\':
            		textBuffer.append("\\\\");
                	valueBuffer.append("\\");
                	break;
            	case 't':
            		textBuffer.append("\\t");
                	valueBuffer.append("\t");
                	break;
            	case '\'':
            		textBuffer.append("\\'");
                	valueBuffer.append("\'");
                	break;
            	case '\"':
            		textBuffer.append("\\\"");
                	valueBuffer.append("\"");
                	break;
            	case EOL:
            		// this is allowed, multi-line strings
            		break;
                default:
                	//invalidEscapeSequence = true;
                	textBuffer.append("\\" + peekChar());
                	valueBuffer.append(peekChar()); // excludes the \
                	
                	// unrepresented currently.
                	// f - formfeed
                	// a - alarm (BEL)
                	// v vertical tab
                	// question mark
                	// octal number 'ooo':  \ooo
                	// hex number 'hh' :  \xhh
            	}
            	
            	nextChar(); // consume the char we used in the switch
            }
            else
            {
            	textBuffer.append(currentChar);
            	valueBuffer.append(currentChar);
            }
            
        	currentChar = nextChar();  // consume character
        }

        if (currentChar == '\"') {
            nextChar();  // consume final quote
            textBuffer.append('\"');

            
            // Here, the error is flagged, after the complete string has been
            // extracted (to prevent future errors due to ending half way
            // through a string)
            
            if (invalidEscapeSequence)
            {
            	type = ERROR;
            	value = UNKNOWN_ESCAPE_SEQUENCE;
            }
            else
            {
            	type = STRING;
                value = valueBuffer.toString();
            }
        }
        else if (currentChar == EOF)
        {
            type = ERROR;
            value = UNEXPECTED_EOF;
        }
        else
        {
        	type = ERROR;
            value = UNEXPECTED_EOL;
        }

        text = textBuffer.toString();
    }
}
